// 
// 为Arduino移植的AM2301温湿度传感器读取的类库
// 该库原来用于G2553单片机
// 本次更新添加了详细的错误提示
// By Christopher Lee. 2015/02/01 Sunday.
// Version 0.2
// 

#include "AM2301Sensor.h"

//
// 公有函数实现
// 调用该函数会进行一次读操作，尝试将当前的温湿度值读取出来
//
// 返回值：
// AM2301LIB_OK						读取数据正常
// AM2301LIB_ERR_TIMEOUT			读取数据超时，可能是信号线没有连接上
// AM2301LIB_ERR_CHECKSUM			在进行校验时发生错误
// AM2301LIB_ERR_SENSOR_NOT_FOUND	没有找到可通信的AM2301传感器
//
int AM2301Sensor::Read(int dataPin)
{
	//
	// 注意管脚号，这个涉及到我们能不能读到数据的关键
	//
	this->dataPin = dataPin;

	int icount = 0, loopCnt = 10000;
	unsigned char tempData[5],//将获取的5组数据存放到该数组中，待校验后进行转换。
		sum = 0;
	int tempHumidity = 0, tempTemperature = 0;
	/*以下为主机和从机的操作*/

	//首先主机将I/O设为输出方向
	pinMode(this->dataPin, OUTPUT);
	//输出1ms的低电平，即拉低总线
	digitalWrite(this->dataPin, LOW);
	delay(1);
	//接下来释放总线，即将方向设为输入，此时总线被上拉电阻拉高
	digitalWrite(this->dataPin, HIGH);
	delayMicroseconds(5);
	pinMode(this->dataPin, INPUT);
	//延时等待约30到40us等待相应信号
	delayMicroseconds(35);
	//如果接收到低电平，表示接收到响应信号的一部分
	if (digitalRead(this->dataPin) == LOW)
	{
		while (digitalRead(this->dataPin) == LOW)	//等待低电平结束，然后检测是否接收到80us的高电平。
		{
			//
			// 检查是否超时
			//
			if (loopCnt-- == 0)
			{
				return AM2301LIB_ERR_TIMEOUT;
			}
		}
		//接下来等待如果有高电平的响应，则等待其结束，准备接受数据，即表明从机响应结束并通知主机接收数据。
		loopCnt = 10000;
		while (digitalRead(this->dataPin) == HIGH)
		{
			//
			// 检查是否超时
			//
			if (loopCnt-- == 0)
			{
				return AM2301LIB_ERR_TIMEOUT;
			}
		}

		for (icount = 0; icount < 5; icount++)
		{
			if (this->ByteRead(&tempData[icount]) == AM2301LIB_OK)
			{
				if (icount <= 3)
				{//把各位加起来，准备校验
					sum += tempData[icount];
				}
			}
			else
			{
				return AM2301LIB_ERR_TIMEOUT;
			}
		}

		//进行校验，如果成功，则进行转换操作。即 湿度高位+湿度低位+温度高位+温度低位 = 校验位
		if (sum == tempData[4])
		{
			tempHumidity = tempData[0];
			tempHumidity <<= 8;
			tempHumidity |= tempData[1];

			tempTemperature = tempData[2];
			tempTemperature <<= 8;
			tempTemperature |= tempData[3];
			//判断温度值是否是负的值， 即判断最高位是否是1
			if (tempTemperature & 0x8000)
			{
				tempTemperature &= 0x7fff;
				tempTemperature = 0 - tempTemperature;
			}

			this->temperature = tempTemperature / 10.0;
			this->humidity = tempHumidity / 10.0;

			return AM2301LIB_OK;//返回转换成功标志。
		}
		else
		{
			return AM2301LIB_ERR_CHECKSUM;
		}
	}
	else
	{
		return AM2301LIB_ERR_SENSOR_NOT_FOUND;
	}
}

//
// 私有函数实现
// 该函数用于读取一个字节的数据出来
//
int AM2301Sensor::ByteRead(byte* data)
{
	int icount = 0,	//计数读取的位
		loopCnt = 10000;	//计数
	unsigned char  bit = 0;
	for (icount = 0; icount < 8; icount++)
	{
		while (digitalRead(this->dataPin) == LOW)	//低电平阶段一直等待高电平的到来，然后判断是0还是1
		{
			//
			// 检查是否超时
			//
			if (loopCnt-- == 0)
			{
				return AM2301LIB_ERR_TIMEOUT;
			}
		}
		//当接收到高电平时，则进行延时等待，约35us等待时间，这样确保正确的读取到0、或者1电平
		delayMicroseconds(40);
		bit = 0;
		//如果此时读到的是高电平，表明是1
		if (digitalRead(this->dataPin) == HIGH)
		{
			bit = 1;
		}

		loopCnt = 10000;
		while (digitalRead(this->dataPin) == HIGH)	//等待高电平的阶段结束，进行下一位数据接收。
		{
			//
			// 检查是否超时
			//
			if (loopCnt-- == 0)
			{
				return AM2301LIB_ERR_TIMEOUT;
			}
		}
		*data <<= 1;
		*data |= bit;
	}
	//将读取到的字节返回。
	return AM2301LIB_OK;
}

